當 Yii 應用程式開始處理請求的 URL 時,它採取的首要步驟是將 URL 解析為路由。然後,路由會被用來實例化相應的控制器動作以處理請求。這個完整過程被稱為路由。
路由的反向過程稱為 URL 建立,它從給定的路由和相關聯的查詢參數建立 URL。當稍後請求建立的 URL 時,路由過程可以將其解析回原始路由和查詢參數。
負責路由和 URL 建立的核心組件是 URL 管理器,它被註冊為 urlManager
應用程式組件。URL 管理器提供了 parseRequest() 方法來將傳入的請求解析為路由和相關聯的查詢參數,以及 createUrl() 方法來從給定的路由及其相關聯的查詢參數建立 URL。
通過在應用程式配置中配置 urlManager
組件,您可以讓應用程式識別任意 URL 格式,而無需修改現有的應用程式程式碼。例如,您可以使用以下程式碼來為 post/view
動作建立 URL
use yii\helpers\Url;
// Url::to() calls UrlManager::createUrl() to create a URL
$url = Url::to(['post/view', 'id' => 100]);
根據 urlManager
配置,建立的 URL 可能看起來像以下其中一種(或其他格式)。如果稍後請求建立的 URL,它仍然會被解析回原始路由和查詢參數值。
/index.php?r=post%2Fview&id=100
/index.php/post/100
/posts/100
URL 管理器 支援兩種 URL 格式
預設 URL 格式使用名為 r
的 查詢參數 來表示路由,並使用普通的查詢參數來表示與路由相關聯的查詢參數。例如,URL /index.php?r=post/view&id=100
表示路由 post/view
和 id
查詢參數 100
。預設 URL 格式不需要對 URL 管理器 進行任何配置,並且可以在任何 Web 伺服器設定中運作。
美化 URL 格式使用入口腳本名稱之後的額外路徑來表示路由和相關聯的查詢參數。例如,URL /index.php/post/100
中的額外路徑是 /post/100
,它可以使用適當的 URL 規則 表示路由 post/view
和 id
查詢參數 100
。要使用美化 URL 格式,您需要根據 URL 應該如何呈現的實際需求設計一組 URL 規則。
您可以通過切換 enablePrettyUrl 屬性來在兩種 URL 格式之間切換 URL 管理器,而無需更改任何其他應用程式程式碼。
路由包含兩個步驟
當使用預設 URL 格式時,將請求解析為路由就像取得名為 r
的 GET
查詢參數的值一樣簡單。
當使用美化 URL 格式時,URL 管理器 將檢查已註冊的 URL 規則,以找到可以將請求解析為路由的匹配規則。如果找不到這樣的規則,將會拋出 yii\web\NotFoundHttpException 異常。
一旦請求被解析為路由,就該建立由路由識別的控制器動作了。路由會被其中的斜線分解為多個部分。例如,site/index
將被分解為 site
和 index
。每個部分都是一個 ID,可以指代模組、控制器或動作。從路由的第一部分開始,應用程式將執行以下步驟來建立模組(如果有的話)、控制器和動作
在上述步驟中,如果發生任何錯誤,將拋出 yii\web\NotFoundHttpException,表明路由過程失敗。
當請求被解析為空路由時,將會使用所謂的預設路由來代替。預設情況下,預設路由是 site/index
,它指代 site
控制器的 index
動作。您可以通過在應用程式配置中配置應用程式的 defaultRoute 屬性來自訂它,如下所示
[
// ...
'defaultRoute' => 'main/index',
];
與應用程式的預設路由類似,模組也有預設路由,因此,例如,如果有一個 user
模組,並且請求被解析為路由 user
,則模組的 defaultRoute 用於確定控制器。預設情況下,控制器名稱為 default
。如果在 defaultRoute 中沒有指定動作,則控制器的 defaultAction 屬性用於確定動作。在本例中,完整路由將是 user/default/index
。
catchAll
路由 ¶有時,您可能想要暫時將您的 Web 應用程式置於維護模式,並為所有請求顯示相同的資訊頁面。有很多方法可以實現這個目標。但是,最簡單的方法之一是在應用程式配置中配置 yii\web\Application::$catchAll 屬性,如下所示
[
// ...
'catchAll' => ['site/offline'],
];
通過上述配置,site/offline
動作將被用於處理所有傳入的請求。
catchAll
屬性應該接受一個陣列,其第一個元素指定路由,其餘元素(名稱-值對)指定要綁定到動作的參數。
資訊:當此屬性啟用時,開發環境中的 debug 工具列 將無法運作。
Yii 提供了一個助手方法 yii\helpers\Url::to(),用於從給定的路由及其相關聯的查詢參數建立各種 URL。例如,
use yii\helpers\Url;
// creates a URL to a route: /index.php?r=post%2Findex
echo Url::to(['post/index']);
// creates a URL to a route with parameters: /index.php?r=post%2Fview&id=100
echo Url::to(['post/view', 'id' => 100]);
// creates an anchored URL: /index.php?r=post%2Fview&id=100#content
echo Url::to(['post/view', 'id' => 100, '#' => 'content']);
// creates an absolute URL: https://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], true);
// creates an absolute URL using the https scheme: https://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], 'https');
請注意,在上面的範例中,我們假設正在使用預設 URL 格式。如果啟用了美化 URL 格式,則建立的 URL 將會不同,這取決於正在使用的 URL 規則。
傳遞給 yii\helpers\Url::to() 方法的路由是上下文相關的。它可以是相對路由或絕對路由,它們將根據以下規則進行正規化
從 2.0.2 版本開始,您可以使用 別名 的形式指定路由。如果是這種情況,別名將首先轉換為實際路由,然後根據上述規則將其轉換為絕對路由。
例如,假設當前模組是 admin
,當前控制器是 post
,
use yii\helpers\Url;
// currently requested route: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['']);
// a relative route with action ID only: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['index']);
// a relative route: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['post/index']);
// an absolute route: /index.php?r=post%2Findex
echo Url::to(['/post/index']);
// using an alias "@posts", which is defined as "/post/index": /index.php?r=post%2Findex
echo Url::to(['@posts']);
yii\helpers\Url::to() 方法是通過調用 createUrl() 和 createAbsoluteUrl() 方法來實現的 URL 管理器。在接下來的幾個小節中,我們將解釋如何配置 URL 管理器 以自訂建立的 URL 的格式。
yii\helpers\Url::to() 方法還支援建立與特定路由無關的 URL。在這種情況下,您應該傳遞一個字串而不是將陣列作為其第一個參數。例如,
use yii\helpers\Url;
// currently requested URL: /index.php?r=admin%2Fpost%2Findex
echo Url::to();
// an aliased URL: https://example.com
Yii::setAlias('@example', 'https://example.com/');
echo Url::to('@example');
// an absolute URL: https://example.com/images/logo.gif
echo Url::to('/images/logo.gif', true);
除了 to()
方法之外,yii\helpers\Url 助手類別還提供了其他幾個方便的 URL 建立方法。例如,
use yii\helpers\Url;
// home page URL: /index.php?r=site%2Findex
echo Url::home();
// the base URL, useful if the application is deployed in a sub-folder of the Web root
echo Url::base();
// the canonical URL of the currently requested URL
// see https://en.wikipedia.org/wiki/Canonical_link_element
echo Url::canonical();
// remember the currently requested URL and retrieve it back in later requests
Url::remember();
echo Url::previous();
要使用美化 URL,請在應用程式配置中配置 urlManager
組件,如下所示
[
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => false,
'rules' => [
// ...
],
],
],
]
enablePrettyUrl 屬性是強制性的,因為它切換美化 URL 格式。其餘屬性是可選的。但是,上面顯示的配置是最常用的。
false
,將產生 URL /post/100
而不是建立 URL /index.php/post/100
。注意:為了在建立的 URL 中隱藏入口腳本名稱,除了將 showScriptName 設定為
false
之外,您可能還需要配置您的 Web 伺服器,以便它可以正確識別當請求的 URL 沒有明確指定 PHP 腳本時,應該執行哪個 PHP 腳本。如果您正在使用 Apache 或 nginx Web 伺服器,您可以參考 安裝 部分中描述的建議配置。
URL 規則是一個類別,它實現了 yii\web\UrlRuleInterface,通常是 yii\web\UrlRule。每個 URL 規則都包含一個模式,用於匹配 URL 的路徑資訊部分、一個路由和一些查詢參數。如果 URL 規則的模式與請求的 URL 匹配,則可以使用該規則來解析請求。如果 URL 規則的路由和查詢參數名稱與給定的路由和查詢參數名稱匹配,則可以使用該規則來建立 URL。
當啟用美化 URL 格式時,URL 管理器 使用其 rules 屬性中聲明的 URL 規則來解析傳入的請求和建立 URL。特別是,為了解析傳入的請求,URL 管理器 按照聲明規則的順序檢查規則,並尋找與請求的 URL 匹配的第一個規則。然後,使用匹配的規則將 URL 解析為路由及其相關聯的參數。類似地,為了建立 URL,URL 管理器 尋找與給定路由和參數匹配的第一個規則,並使用該規則來建立 URL。
您可以將 yii\web\UrlManager::$rules 配置為陣列,其中鍵是 模式,值是相應的 路由。每個模式-路由對都建構一個 URL 規則。例如,以下 規則 配置聲明了兩個 URL 規則。第一個規則匹配 URL posts
並將其映射到路由 post/index
。第二個規則匹配與正則表達式 post/(\d+)
匹配的 URL,並將其映射到路由 post/view
,並定義一個名為 id
的查詢參數。
'rules' => [
'posts' => 'post/index',
'post/<id:\d+>' => 'post/view',
]
資訊:規則中的模式用於匹配 URL 的路徑資訊部分。例如,
/index.php/post/100?source=ad
的路徑資訊是post/100
(忽略前導和尾隨斜線),它與模式post/(\d+)
匹配。
除了將 URL 規則聲明為模式-路由對之外,您還可以將它們聲明為配置陣列。每個配置陣列都用於配置單個 URL 規則物件。當您想要配置 URL 規則的其他屬性時,通常需要這樣做。例如,
'rules' => [
// ...other url rules...
[
'pattern' => 'posts',
'route' => 'post/index',
'suffix' => '.json',
],
]
預設情況下,如果您沒有為規則配置指定 class
選項,它將採用預設類別 yii\web\UrlRule,這是 yii\web\UrlManager::$ruleConfig 中定義的預設值。
URL 規則可以與具名查詢參數相關聯,這些參數在模式中以 <ParamName:RegExp>
的格式指定,其中 ParamName
指定參數名稱,RegExp
是用於匹配參數值的可選正則表達式。如果未指定 RegExp
,則表示參數值應為不帶任何斜線的字串。
注意:您只能在參數內部使用正則表達式。模式的其餘部分被視為純文字。
當規則用於解析 URL 時,它將使用與 URL 的相應部分匹配的值填充相關聯的參數,並且這些參數稍後將通過 request
應用程式組件在 $_GET
中可用。當規則用於建立 URL 時,它將取得提供的參數的值,並將它們插入到聲明參數的位置。
讓我們使用一些範例來說明具名參數如何運作。假設我們聲明了以下三個 URL 規則
'rules' => [
'posts/<year:\d{4}>/<category>' => 'post/index',
'posts' => 'post/index',
'post/<id:\d+>' => 'post/view',
]
當規則用於解析 URL 時
/index.php/posts
使用第二個規則解析為路由 post/index
;/index.php/posts/2014/php
使用第一個規則解析為路由 post/index
,year
參數的值為 2014,category
參數的值為 php
;/index.php/post/100
使用第三個規則解析為路由 post/view
,id
參數的值為 100;true
時,/index.php/posts/php
將導致 yii\web\NotFoundHttpException,因為它與任何模式都不匹配。如果 yii\web\UrlManager::$enableStrictParsing 為 false
(預設值),則路徑資訊部分 posts/php
將作為路由返回。如果存在相應的動作,這將執行該動作,否則將拋出 yii\web\NotFoundHttpException。當規則用於建立 URL 時
Url::to(['post/index'])
使用第二個規則建立 /index.php/posts
;Url::to(['post/index', 'year' => 2014, 'category' => 'php'])
使用第一個規則建立 /index.php/posts/2014/php
;Url::to(['post/view', 'id' => 100])
使用第三個規則建立 /index.php/post/100
;Url::to(['post/view', 'id' => 100, 'source' => 'ad'])
使用第三個規則建立 /index.php/post/100?source=ad
。由於 source
參數未在規則中指定,因此它會作為查詢參數附加在建立的 URL 中。Url::to(['post/index', 'category' => 'php'])
使用任何規則都無法建立 /index.php/post/index?category=php
。請注意,由於沒有任何規則適用,因此 URL 的建立方式只是將路由作為路徑資訊附加,並將所有參數作為查詢字串部分附加。您可以將參數名稱嵌入 URL 規則的路由中。這允許 URL 規則用於匹配多個路由。例如,以下規則將 controller
和 action
參數嵌入到路由中。
'rules' => [
'<controller:(post|comment)>/create' => '<controller>/create',
'<controller:(post|comment)>/<id:\d+>/<action:(update|delete)>' => '<controller>/<action>',
'<controller:(post|comment)>/<id:\d+>' => '<controller>/view',
'<controller:(post|comment)>s' => '<controller>/index',
]
為了剖析 URL /index.php/comment/100/update
,將會套用第二條規則,此規則會將 controller
參數設定為 comment
,並將 action
參數設定為 update
。因此,路由 <controller>/<action>
會解析為 comment/update
。
同樣地,為了替路由 comment/index
建立 URL,將會套用最後一條規則,此規則會建立一個 URL /index.php/comments
。
資訊:透過參數化路由,可以大幅減少 URL 規則的數量,這可以顯著提升 URL 管理器 的效能。
預設情況下,規則中宣告的所有參數都是必要的。如果請求的 URL 未包含特定參數,或者在建立 URL 時缺少特定參數,則該規則將不會套用。若要使某些參數成為選填的,您可以設定規則的 defaults 屬性。在此屬性中列出的參數是選填的,並且在未提供時將採用指定的值。
在以下規則宣告中,page
和 tag
參數都是選填的,並且在未提供時將分別採用值 1 和空字串。
'rules' => [
// ...other rules...
[
'pattern' => 'posts/<page:\d+>/<tag>',
'route' => 'post/index',
'defaults' => ['page' => 1, 'tag' => ''],
],
]
上述規則可用於剖析或建立以下任何 URL
/index.php/posts
:page
為 1,tag
為 ''。/index.php/posts/2
:page
為 2,tag
為 ''。/index.php/posts/2/news
:page
為 2,tag
為 'news'
。/index.php/posts/news
:page
為 1,tag
為 'news'
。如果不使用選填參數,您將必須建立 4 條規則才能達到相同的結果。
注意:如果 pattern 僅包含選填參數和斜線,則只有在省略所有其他參數時,才能省略第一個參數。
可以在 URL 規則的 pattern 中包含 Web 伺服器名稱。當您的應用程式需要針對不同的 Web 伺服器名稱有不同的行為時,這會特別有用。例如,以下規則會將 URL https://admin.example.com/login
剖析為路由 admin/user/login
,並將 https://www.example.com/login
剖析為 site/login
。
'rules' => [
'https://admin.example.com/login' => 'admin/user/login',
'https://www.example.com/login' => 'site/login',
]
您也可以在伺服器名稱中嵌入參數,以從中提取動態資訊。例如,以下規則會將 URL https://en.example.com/posts
剖析為路由 post/index
和參數 language=en
。
'rules' => [
'https://<language:\w+>.example.com/posts' => 'post/index',
]
自 2.0.11 版本起,您也可以使用協定相對 pattern,其適用於 http
和 https
。語法與上述相同,但省略 http:
部分,例如:'//www.example.com/login' => 'site/login'
。
注意:包含伺服器名稱的規則不應在其 pattern 中包含入口腳本的子資料夾。例如,如果應用程式的入口腳本位於
https://www.example.com/sandbox/blog/index.php
,則您應該使用 patternhttps://www.example.com/posts
而不是https://www.example.com/sandbox/blog/posts
。這將允許您的應用程式部署在任何目錄下,而無需變更您的 URL 規則。Yii 將自動偵測應用程式的基礎 URL。
您可能希望為了各種目的在 URL 中新增後綴。例如,您可以將 `.html` 新增到 URL,使其看起來像靜態 HTML 頁面的 URL;您也可以將 `.json` 新增到 URL,以指示回應的預期內容類型。您可以透過在應用程式設定中設定 yii\web\UrlManager::$suffix 屬性來實現此目標。
[
// ...
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
// ...
'suffix' => '.html',
'rules' => [
// ...
],
],
],
]
上述設定將允許 URL 管理器 識別請求的 URL,並建立以 `.html` 作為後綴的 URL。
提示:您可以將 `/` 設定為 URL 後綴,使所有 URL 都以斜線結尾。
注意:當您設定 URL 後綴時,如果請求的 URL 沒有後綴,則會被視為無法識別的 URL。這是 SEO(搜尋引擎最佳化)的建議做法,以避免在不同的 URL 上出現重複內容。
有時您可能希望針對不同的 URL 使用不同的後綴。這可以透過設定個別 URL 規則的 suffix 屬性來實現。當 URL 規則設定了此屬性時,它將覆寫 URL 管理器 層級的後綴設定。例如,以下設定包含一個自訂的 URL 規則,它使用 `.json` 作為其後綴,而不是全域的 `.html` 後綴。
[
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
// ...
'suffix' => '.html',
'rules' => [
// ...
[
'pattern' => 'posts',
'route' => 'post/index',
'suffix' => '.json',
],
],
],
],
]
在實作 RESTful API 時,通常需要根據使用的 HTTP 方法將相同的 URL 剖析為不同的路由。這可以透過在規則的 pattern 前面加上支援的 HTTP 方法來輕鬆實現。如果規則支援多個 HTTP 方法,請用逗號分隔方法名稱。例如,以下規則具有相同的 pattern post/<id:\d+>
,但支援不同的 HTTP 方法。對 PUT post/100
的請求將被剖析為 post/update
,而對 GET post/100
的請求將被剖析為 post/view
。
'rules' => [
'PUT,POST post/<id:\d+>' => 'post/update',
'DELETE post/<id:\d+>' => 'post/delete',
'post/<id:\d+>' => 'post/view',
]
注意:如果 URL 規則在其 pattern 中包含 HTTP 方法,則該規則僅用於剖析目的,除非
GET
包含在指定的動詞中。當呼叫 URL 管理器 來建立 URL 時,將會跳過該規則。
提示:為了簡化 RESTful API 的路由,Yii 提供了一個特殊的 URL 規則類別 yii\rest\UrlRule,它非常有效率,並支援一些進階功能,例如自動將 controller ID 複數化。如需更多詳細資訊,請參閱 RESTful API 章節中的 路由 區段。
URL 規則可以動態新增到 URL 管理器。可重新發布的 模組 通常需要這樣做,因為它們想要管理自己的 URL 規則。為了使動態新增的規則在路由過程中生效,您應該在應用程式的 引導啟動 階段新增它們。對於模組,這表示它們應該實作 yii\base\BootstrapInterface 介面,並在 bootstrap() 方法中新增規則,如下所示:
public function bootstrap($app)
{
$app->getUrlManager()->addRules([
// rule declarations here
], false);
}
請注意,您也應該在 yii\web\Application::bootstrap() 中列出這些模組,以便它們可以參與 引導啟動 過程。
儘管預設的 yii\web\UrlRule 類別對於大多數專案來說已經足夠彈性,但在某些情況下,您必須建立自己的規則類別。例如,在汽車經銷商網站中,您可能想要支援類似 `/Manufacturer/Model` 的 URL 格式,其中 `Manufacturer` 和 `Model` 都必須符合儲存在資料庫表格中的某些資料。預設的規則類別在這裡將無法運作,因為它依賴靜態宣告的 pattern。
我們可以建立以下 URL 規則類別來解決這個問題。
<?php
namespace app\components;
use yii\web\UrlRuleInterface;
use yii\base\BaseObject;
class CarUrlRule extends BaseObject implements UrlRuleInterface
{
public function createUrl($manager, $route, $params)
{
if ($route === 'car/index') {
if (isset($params['manufacturer'], $params['model'])) {
return $params['manufacturer'] . '/' . $params['model'];
} elseif (isset($params['manufacturer'])) {
return $params['manufacturer'];
}
}
return false; // this rule does not apply
}
public function parseRequest($manager, $request)
{
$pathInfo = $request->getPathInfo();
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) {
// check $matches[1] and $matches[3] to see
// if they match a manufacturer and a model in the database.
// If so, set $params['manufacturer'] and/or $params['model']
// and return ['car/index', $params]
}
return false; // this rule does not apply
}
}
並在 yii\web\UrlManager::$rules 設定中使用新的規則類別。
'rules' => [
// ...other rules...
[
'class' => 'app\components\CarUrlRule',
// ...configure other properties...
],
]
自 2.0.10 版本起,UrlManager 可以設定為使用 UrlNormalizer 來處理相同 URL 的變體,例如帶有和不帶有尾部斜線的 URL。因為從技術上來說,`https://example.com/path` 和 `https://example.com/path/` 是不同的 URL,為兩者提供相同的內容可能會降低 SEO 排名。預設情況下,正規化器會摺疊連續斜線,根據後綴是否具有尾部斜線來新增或移除尾部斜線,並使用 永久重新導向 重新導向到正規化版本的 URL。正規化器可以針對 URL 管理器全域設定,也可以針對每個規則個別設定 - 預設情況下,每個規則都將使用來自 URL 管理器的正規化器。您可以將 UrlRule::$normalizer 設定為 `false`,以停用特定 URL 規則的正規化。
以下顯示 UrlNormalizer 的範例設定:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => true,
'suffix' => '.html',
'normalizer' => [
'class' => 'yii\web\UrlNormalizer',
// use temporary redirection instead of permanent for debugging
'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,
],
'rules' => [
// ...other rules...
[
'pattern' => 'posts',
'route' => 'post/index',
'suffix' => '/',
'normalizer' => false, // disable normalizer for this rule
],
[
'pattern' => 'tags',
'route' => 'tag/index',
'normalizer' => [
// do not collapse consecutive slashes for this rule
'collapseSlashes' => false,
],
],
],
]
注意:預設情況下,UrlManager::$normalizer 已停用。您需要明確地設定它才能啟用 URL 正規化。
在開發複雜的 Web 應用程式時,最佳化 URL 規則非常重要,這樣可以減少剖析請求和建立 URL 所需的時間。
透過使用參數化路由,您可以減少 URL 規則的數量,這可以顯著提升效能。
在剖析或建立 URL 時,URL 管理器 會依照宣告順序檢查 URL 規則。因此,您可以考慮調整 URL 規則的順序,以便將更具體和/或更常用的規則放在較少使用的規則之前。
如果某些 URL 規則在其 pattern 或路由中共享相同的前綴,您可以考慮使用 yii\web\GroupUrlRule,以便 URL 管理器 可以更有效率地將它們作為一個群組來檢查。當您的應用程式由模組組成時,通常會是這種情況,每個模組都有自己的一組 URL 規則,並以模組 ID 作為它們的共同前綴。
發現錯字或您認為此頁面需要改進嗎?
在 Github 上編輯 !
註冊 或 登入 以進行評論。